package com.movitech.mobile.cz.base.modules.activiti.service;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.activiti.bpmn.BpmnAutoLayout;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FieldExtension;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.task.Task;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.movitech.mobile.cz.base.common.utils.ActivitiConstant;
import com.movitech.mobile.cz.base.common.utils.ActivitiUtils;
import com.movitech.mobile.cz.base.common.utils.DateUtils;
import com.movitech.mobile.cz.base.common.utils.FileUtils;
import com.movitech.mobile.cz.base.modules.activiti.entity.AProcessConfig;
import com.movitech.mobile.cz.base.modules.activiti.entity.AProcessNodeConfig;
import com.movitech.mobile.cz.base.modules.activiti.entity.AProcessNodeLineConfig;

@Service
public class ActivitiService{
    protected Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private ActivitiRepositoryService activitiRepositoryService;
	@Autowired
	private ActivitiRuntimeService activitiRuntimeService;
	@Autowired
	private ActivitiTaskService activitiTaskService;
	
	
    /***
     * 生成Bpmn并部署
     * @param aProcessConfig
     * @param lstAProcessNodeConfig
     * @param lstAProcessNodeLineConfig
     * @param filePath
     * @return
     */
    public Deployment generateAndDeployBpm(AProcessConfig aProcessConfig,List<AProcessNodeConfig> lstAProcessNodeConfig,List<AProcessNodeLineConfig> lstAProcessNodeLineConfig) {
    	Deployment deployment = null;
    	try {
			BpmnModel bpmnModel = constructBpmnModel(aProcessConfig, lstAProcessNodeConfig, lstAProcessNodeLineConfig);
			if(bpmnModel!=null) {
				///Deploy the process to the engine
			    deployment = activitiRepositoryService.deployBpmnModel(bpmnModel, aProcessConfig.getProcessKey(), aProcessConfig.getProcessKey());

			    ///保存流程节点配置与流程任务的关联关系
//			    saveAProcessConfigNodeAndTaskRelations(lstAProcessNodeConfig, bpmnModel);
			}else{
				logger.info("===========com.movitech.mobile.modules.common.ActivitiService.generateAndDeployBpm====[AProcessConfig ID]"+aProcessConfig.getId()+"===BpmnModel is null");
			}
		} catch (Exception e) {
			logger.info("===========com.movitech.mobile.modules.common.ActivitiService.generateAndDeployBpm====[ERROR]======"+e.getMessage());
			e.printStackTrace();
		}
    	return deployment;
    }
    
    /***
     * 生成Bpmn文件
     * @param aProcessConfig
     * @param lstAProcessNodeConfig
     * @param lstAProcessNodeLineConfig
     * @param filePath
     * @return
     */
    public boolean generateBpmFile(AProcessConfig aProcessConfig,List<AProcessNodeConfig> lstAProcessNodeConfig,List<AProcessNodeLineConfig> lstAProcessNodeLineConfig,String filePath) {
    	boolean isDone = false;
    	try {
			BpmnModel bpmnModel = constructBpmnModel(aProcessConfig, lstAProcessNodeConfig, lstAProcessNodeLineConfig);
			if(bpmnModel!=null) {
				byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(bpmnModel);
				FileUtils.writeByteArrayToFile(new File(filePath+aProcessConfig.getProcessKey()+".bpmn"), bpmnBytes);
				isDone = true;
			}else{
				logger.info("===========com.movitech.mobile.modules.common.ActivitiService.generateBpmFile====[AProcessConfig ID]"+aProcessConfig.getId()+"===BpmnModel is null");
			}
		} catch (Exception e) {
			logger.info("===========com.movitech.mobile.modules.common.ActivitiService.generateBpmFile====[ERROR]======"+e.getMessage());
			e.printStackTrace();
		}
    	return isDone;
    }
    
    /***
     * 生成Bpmn文件并部署
     * @param aProcessConfig
     * @param lstAProcessNodeConfig
     * @param lstAProcessNodeLineConfig
     * @param filePath
     * @return
     */
    public Deployment generateFileAndDeployBpm(AProcessConfig aProcessConfig,List<AProcessNodeConfig> lstAProcessNodeConfig,List<AProcessNodeLineConfig> lstAProcessNodeLineConfig,String filePath) {
    	Deployment deployment = null;
    	try {
			BpmnModel bpmnModel = constructBpmnModel(aProcessConfig, lstAProcessNodeConfig, lstAProcessNodeLineConfig);
			if(bpmnModel!=null) {
				byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(bpmnModel);
				FileUtils.writeByteArrayToFile(new File(filePath+aProcessConfig.getProcessKey()+"_"+DateUtils.getNowString()+".bpmn"), bpmnBytes);

				///Deploy the process to the engine
			    deployment = activitiRepositoryService.deployBpmnModel(bpmnModel, aProcessConfig.getProcessKey(), aProcessConfig.getProcessKey());
			    
			    ///保存流程节点配置与流程任务的关联关系
//			    saveAProcessConfigNodeAndTaskRelations(lstAProcessNodeConfig, bpmnModel);
			}else{
				logger.info("===========com.movitech.mobile.modules.common.ActivitiService.generateFileAndDeployBpm====[AProcessConfig ID]"+aProcessConfig.getId()+"===BpmnModel is null");
			}
		} catch (Exception e) {
			logger.info("===========com.movitech.mobile.modules.common.ActivitiService.generateFileAndDeployBpm====[ERROR]======"+e.getMessage());
			e.printStackTrace();
		}
    	return deployment;
    }
    
    /**
     * 构建BpmnModel对象
     * @param aProcessConfig
     * @param lstAProcessNodeConfig
     * @param lstAProcessNodeLineConfig
     * @return
     */
    private BpmnModel constructBpmnModel(AProcessConfig aProcessConfig,List<AProcessNodeConfig> lstAProcessNodeConfig,List<AProcessNodeLineConfig> lstAProcessNodeLineConfig) {
    	BpmnModel bpmnModel = null;
    	if(aProcessConfig != null && !CollectionUtils.isEmpty(lstAProcessNodeConfig) && !CollectionUtils.isEmpty(lstAProcessNodeLineConfig)) {
    		bpmnModel = new BpmnModel();
    		
    		//
    		Process process = new Process();  
    		bpmnModel.addProcess(process);
    		process.setId(aProcessConfig.getProcessKey());
    		process.setName(aProcessConfig.getProcessName());
    		
    		//node
    		for (AProcessNodeConfig aProcessNodeConfig : lstAProcessNodeConfig) {
    			boolean isUsed = false;
				for (AProcessNodeLineConfig aProcessNodeLineConfig : lstAProcessNodeLineConfig) {
					if(StringUtils.equalsIgnoreCase(aProcessNodeConfig.getId(),aProcessNodeLineConfig.getFromNodeConfigId()) || StringUtils.equalsIgnoreCase(aProcessNodeConfig.getId(),aProcessNodeLineConfig.getToNodeConfigId())) {
						isUsed = true;
						break;
					}
				}
    			////
				if(isUsed) {
					if(NumberUtils.toInt(aProcessNodeConfig.getNodeType()+"") == ActivitiConstant.NODE_TYPE_STARTEVENT) {//开始节点
						process.addFlowElement(ActivitiUtils.createStartEvent(aProcessNodeConfig.getNodeKey(),aProcessNodeConfig.getNodeName()));
						
					}else if(NumberUtils.toInt(aProcessNodeConfig.getNodeType()+"") == ActivitiConstant.NODE_TYPE_TASK) {//task任务
						if(NumberUtils.toInt(aProcessNodeConfig.getNodeUserGroupsType()+"") == ActivitiConstant.NODE_USER_TYPE_USERS) {
							if(NumberUtils.toInt(aProcessNodeConfig.getNodeCountsignFlag() + "") == ActivitiConstant.YES_INTEGER){
								process.addFlowElement(ActivitiUtils.createAssigneeTask(aProcessNodeConfig.getNodeKey(), aProcessNodeConfig.getNodeName(), aProcessNodeConfig.getNodeUserGroups(),aProcessNodeConfig.getNodeCountsignFlag()));
							}else {
								process.addFlowElement(ActivitiUtils.createCandidateUserTask(aProcessNodeConfig.getNodeKey(), aProcessNodeConfig.getNodeName(), aProcessNodeConfig.getNodeUserGroups()));
							}
						}else if(NumberUtils.toInt(aProcessNodeConfig.getNodeUserGroupsType()+"") == ActivitiConstant.NODE_USER_TYPE_USERGROUP) {
							process.addFlowElement(ActivitiUtils.createCandidateGroupsTask(aProcessNodeConfig.getNodeKey(), aProcessNodeConfig.getNodeName(), aProcessNodeConfig.getNodeUserGroups()));
						}else {
							process.addFlowElement(ActivitiUtils.createNoUserTask(aProcessNodeConfig.getNodeKey(), aProcessNodeConfig.getNodeName()));
						}
					}else if(NumberUtils.toInt(aProcessNodeConfig.getNodeType()+"") == ActivitiConstant.NODE_TYPE_EXCLUSIVEGATEWAY) {//exclusiveGateway排他网关
						process.addFlowElement(ActivitiUtils.createExclusiveGateway(aProcessNodeConfig.getNodeKey(),aProcessNodeConfig.getNodeName()));
						
					}else if(NumberUtils.toInt(aProcessNodeConfig.getNodeType()+"") == ActivitiConstant.NODE_TYPE_PARALLELGATEWAY) {//parallelGateway并行网关
						process.addFlowElement(ActivitiUtils.createParallelGateway(aProcessNodeConfig.getNodeKey(),aProcessNodeConfig.getNodeName()));
						
					}else if(NumberUtils.toInt(aProcessNodeConfig.getNodeType()+"") == ActivitiConstant.NODE_TYPE_ENDEVENT) {//结束节点
						process.addFlowElement(ActivitiUtils.createEndEvent(aProcessNodeConfig.getNodeKey(),aProcessNodeConfig.getNodeName()));
					}
				}
			}
    		
    		//line
			for (AProcessNodeLineConfig aProcessNodeLineConfig : lstAProcessNodeLineConfig) {
    			if(StringUtils.isNotEmpty(aProcessNodeLineConfig.getFromNodeConfigId()) && StringUtils.isNotEmpty(aProcessNodeLineConfig.getToNodeConfigId())) {
    				///获取from/to节点对应的key
    				String fromKey = "";
        			String endKey = "";
        			for (AProcessNodeConfig aProcessNodeConfig : lstAProcessNodeConfig) {
        				if(StringUtils.equalsIgnoreCase(aProcessNodeLineConfig.getFromNodeConfigId(), aProcessNodeConfig.getId())) {
        					fromKey = aProcessNodeConfig.getNodeKey();
        				}
        				if(StringUtils.equalsIgnoreCase(aProcessNodeLineConfig.getToNodeConfigId(), aProcessNodeConfig.getId())) {
        					endKey = aProcessNodeConfig.getNodeKey();
        				}
        				
        				if(StringUtils.isNotEmpty(fromKey) && StringUtils.isNotEmpty(endKey)) {
        					break;
        				}
        			}
        			
        			///创建线
//        			if(StringUtils.isNotEmpty(fromKey) && StringUtils.isNotEmpty(endKey)) {
//        		        process.addFlowElement(ActivitiUtils.createSequenceFlow(fromKey, endKey, aProcessNodeLineConfig.getLineName(), aProcessNodeLineConfig.getLineConditionexpression()));   
//        			}
        			if(StringUtils.isNotEmpty(fromKey) && StringUtils.isNotEmpty(endKey)) {
        				List<FieldExtension> lstFieldExtensions = new ArrayList<FieldExtension>();
        				FieldExtension fieldExtension = new FieldExtension();
        				fieldExtension.setFieldName("lineConfigId");
        				fieldExtension.setStringValue(aProcessNodeLineConfig.getId());
        				lstFieldExtensions.add(fieldExtension);
        		        process.addFlowElement(ActivitiUtils.createSequenceFlow(fromKey, endKey, aProcessNodeLineConfig.getLineName(), aProcessNodeLineConfig.getLineConditionexpression(),"take","com.movitech.mobile.cz.base.modules.activiti.listener.FlowStatusExecutionListener",lstFieldExtensions));   
        			}
    			} 
			}

			// 2. Generate graphical information
			new BpmnAutoLayout(bpmnModel).execute();
    	}
    	return bpmnModel;
    }
    
    /***
     *  保存流程节点配置与流程任务的关联关系
     * @param lstAProcessNodeConfig
     * @param bpmnModel
     */
    @SuppressWarnings("unused")
    public void saveAProcessConfigNodeAndTaskRelations(List<AProcessNodeConfig> lstAProcessNodeConfig,BpmnModel bpmnModel) {
		Collection<FlowElement> collectionFlowElements = getBpmnModelFlowElementList(bpmnModel);
//    	for (AProcessNodeConfig aProcessNodeConfig : lstAProcessNodeConfig) {
//    		boolean isExist = false;
//    		for (FlowElement flowElement : collectionFlowElements) {
//    			if(StringUtils.equalsIgnoreCase(flowElement.getId(), aProcessNodeConfig.getNodeKey())) {
//    				
//    				isExist = true;
//    				break;
//    			}
//			}
//		}
    	
    }
    
    /***
     * 获取流程定义中所有的节点
     * @param bpmnModel
     */
    public Collection<FlowElement> getBpmnModelFlowElementList(BpmnModel bpmnModel) {
    	Collection<FlowElement> collectionFlowElements = null;
		if(bpmnModel != null) {  
			collectionFlowElements = bpmnModel.getMainProcess().getFlowElements();  
		    for(FlowElement flowElement : collectionFlowElements) {  
		        System.out.println("flowelement id:" + flowElement.getId() + "  name:" + flowElement.getName() + "   class:" + flowElement.getClass().toString());  
		    }  
		}  
		return collectionFlowElements;
    }
    
    /***
     * 根据id，获取流程任务是实例
     * @param taskId
     * @return
     * @throws Exception 
     */
    public Task getTaskById(String taskId) throws Exception {
    	return activitiTaskService.getTaskById(taskId);
    }
    
    /***
	 * 根据流程实例id，获取当前Task任务列表
	 * 
	 * @param processInstanceId
	 * @return
	 */
	public List<Task> getTasksByProcessInstanceId(String processInstanceId) {
		return activitiTaskService.getTasksByProcessInstanceId(processInstanceId);
	}
}
